﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Sandy
{
    public class OrderPreservingDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary
    {
        private Dictionary<TKey, TValue> _internalDict = new Dictionary<TKey, TValue>();
        private List<TKey> _keyList = new List<TKey>();

        public OrderPreservingDictionary()
        {
            //
        }

        #region IDictionary<TKey,TValue>
        public bool ContainsKey(TKey key)
        {
            return _internalDict.ContainsKey(key);
        }

        public void Add(TKey key, TValue value)
        {
            // Add to dictionary, this will throw if the key exists already
            _internalDict.Add(key, value);

            // If we're here, we added it successfully
            _keyList.Add(key);
        }

        public bool Remove(TKey key)
        {
            bool removed = _internalDict.Remove(key);

            if (removed)
            {
                // We need to remove it from the list as well
                _keyList.Remove(key);
            }

            return removed;
        }

        public bool TryGetValue(TKey key, out TValue value)
        {
            return _internalDict.TryGetValue(key, out value);
        }

        public TValue this[TKey key]
        {
            get { return _internalDict[key]; }
            set
            {
                if (_internalDict.ContainsKey(key))
                {
                    // Order should stay preserved
                    _internalDict[key] = value;
                }
                else
                {
                    // Add it, so that the key is at the end of the list
                    Add(key, value);
                }
            }
        }

        public ICollection<TKey> Keys
        {
            get { return _keyList; }
        }

        public ICollection<TValue> Values
        {
            get
            {
                return Keys.Select(k => _internalDict[k]).ToList();
            }
        }

        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
        {
            return Keys.Select(k => new KeyValuePair<TKey, TValue>(k, _internalDict[k])).GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public void Add(KeyValuePair<TKey, TValue> item)
        {
            Add(item.Key, item.Value);
        }

        public void Clear()
        {
            _internalDict.Clear();
            _keyList.Clear();
        }

        public bool Contains(KeyValuePair<TKey, TValue> item)
        {
            return _internalDict.Contains(item);
        }

        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
        {
            foreach (var kvpair in this)
            {
                array[arrayIndex] = kvpair;
                arrayIndex++;
            }
        }

        public bool Remove(KeyValuePair<TKey, TValue> item)
        {
            return Remove(item.Key);
        }


        public int Count
        {
            get { return _internalDict.Count; }
        }

        public bool IsReadOnly
        {
            get { return false; }
        } 
        #endregion

        #region Implementation of IDictionary

        public bool Contains(object key)
        {
            return this.ContainsKey((TKey) key);
        }

        public void Add(object key, object value)
        {
            this.Add((TKey) key, (TValue) value);
        }

        IDictionaryEnumerator IDictionary.GetEnumerator()
        {
            throw new NotImplementedException();
        }

        public void Remove(object key)
        {
            this.Remove((TKey) key);
        }

        object IDictionary.this[object key]
        {
            get { return this[(TKey) key]; }
            set { this[(TKey) key] = (TValue) value; }
        }

        ICollection IDictionary.Keys
        {
            get { return (ICollection) this.Keys; }
        }

        ICollection IDictionary.Values
        {
            get { return (ICollection) this.Values; }
        }

        public bool IsFixedSize
        {
            get { return false; }
        }

        #endregion

        #region Implementation of ICollection

        public void CopyTo(Array array, int index)
        {
            throw new NotImplementedException();
        }

        public object SyncRoot
        {
            get { throw new NotImplementedException(); }
        }

        public bool IsSynchronized
        {
            get { throw new NotImplementedException(); }
        }

        #endregion

        class DictionaryEnumerator : IDictionaryEnumerator
        {
            private readonly OrderPreservingDictionary<TKey, TValue> _dict;
            private IEnumerator<KeyValuePair<TKey, TValue>> _enumerator;

            public DictionaryEnumerator(OrderPreservingDictionary<TKey, TValue> dict)
            {
                _dict = dict;
                _enumerator = dict.GetEnumerator();
            }

            #region Implementation of IEnumerator

            public bool MoveNext()
            {
                return _enumerator.MoveNext();
            }

            public void Reset()
            {
                _enumerator.Reset();
            }

            public object Current
            {
                get { return Entry; }
            }

            private KeyValuePair<TKey, TValue> CurrentPair
            {
                get
                {
                    return _enumerator.Current;
                }
            }

            #endregion

            #region Implementation of IDictionaryEnumerator

            public object Key
            {
                get { return CurrentPair.Key; }
            }

            public object Value
            {
                get { return CurrentPair.Value; }
            }

            public DictionaryEntry Entry
            {
                get { return new DictionaryEntry(Key,Value); }
            }

            #endregion
        }

    }
}
